package com.breaktian.network.http;



import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.breaktian.network.util.Utils;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by keke.tian on 2018/4/3.
 */

public class Http {
    private static final int METHOD_GET = 0x1;
    private static final int METHOD_POST = 0x2;

    private static final int METHOD_PUT = 0x3;
    private static final int METHOD_DELETE = 0x4;
    private static final int METHOD_HEAD = 0x8;
    private static final int METHOD_PATCH = 0x10;
    public static boolean isDebug = true;
    public static String TAG = "http";


    private HttpParam mParam;
    private HttpHeader mHeader;

    private String mUrl;
    private int mMethod;
    private Object mTag;

    private Handler mMainHandler = new Handler(Looper.getMainLooper());
    private TypeReference<?> mTargetTypeReference;
    private Class<?> mTargetClazz;
    private Cache mCache;


    public Http(String url,int method){
        this.mUrl = url;
        this.mMethod = method;

        mParam = new HttpParam();
        mHeader = new HttpHeader();
    }

    /**
     * 初始化HttpClient
     * @param httpConfig
     */
    public static void init(HttpConfig httpConfig){
        HttpClient.init(httpConfig);
        Cache.init(httpConfig.mContext,10*1024*1024,3*24*60*60*1000);
        //清除缓存
        Cache.clearCache();
    }

    public static Http get(String url) {
        return new Http(Utils.encodeUrlQuery(url), METHOD_GET);
    }

    public static Http post(String url) {
        return new Http(Utils.encodeUrlQuery(url), METHOD_POST);
    }

    public static Http put(@NonNull String url) {
        return new Http(url, METHOD_PUT);
    }

    public static Http delete(@NonNull String url) {
        return new Http(url, METHOD_DELETE);
    }

    public static Http head(@NonNull String url) {
        return new Http(url, METHOD_HEAD);
    }

    public static Http patch(@NonNull String url) {
        return new Http(url, METHOD_PATCH);
    }

    public Http param(String key,String value){
        mParam.add(key,value);
        return this;
    }

    public Http param(HttpParam httpParam){
        mParam.merge(httpParam);
        return this;
    }

    public Http param(Map<String,String> vals){
        mParam.add(vals);
        return this;
    }

    public Http param(String key, File file,String fileName){
        mParam.add(key,file,fileName);
        return this;
    }

    public Map<String, String> getParams(){
        return mParam.parameters();
    }

    /**
     * post json
     * */
    public Http asJson(){
        mParam.asJson();
        return this;
    }

    /**
     * post
     * */
    public Http asDefault(){
        mParam.asDefault();
        return this;
    }

    /**
     * 文件上传
     * */
    public Http asMulti(){
        mParam.asMulti();
        return this;
    }



    public Http header(String key,String value){
        mHeader.add(key,value);
        return this;
    }

    public Http header(HttpHeader httpHeader){
        mHeader.merge(httpHeader);
        return this;
    }

    public Http tag(Object tag){
        mTag = tag;
        return this;
    }

    public Http url(String url){
        mUrl = url;
        return this;
    }

    /**
     * 异步请求
     * @param callback
     * @return
     */
    public <T> T enqueue(@NonNull TypeReference<T> reference, @Nullable RequestCallback<T> callback) {
        mTargetTypeReference = reference;
        return realEnqueue(callback);
    }

    /**
     * 异步请求
     * @param clz
     * @param callback
     * @param <T>
     * @return
     */
    public <T> T enqueue(@NonNull Class<T> clz, @Nullable RequestCallback<T> callback) {
        mTargetClazz = clz;
        return realEnqueue(callback);
    }

    /**
     * 同步请求
     * @param reference
     * @param <T>
     * @return
     */
    public <T> T execute(@NonNull TypeReference<T> reference) {
        mTargetTypeReference = reference;
        return realExecute();
    }

    /**
     * 同步请求
     * @param clz
     * @param <T>
     * @return
     */
    public <T> T execute(@NonNull Class<T> clz) {
        mTargetClazz = clz;
        return realExecute();
    }

    /**
     * 异步请求
     * @param callback
     * @return
     * */
    private <T> T realEnqueue(final RequestCallback<T> callback){
        if(callback == null){
            Utils.e("RequestCallback is null");
            return null;
        }
        if(METHOD_GET == mMethod){//只有get方法才有缓存
            mCache = new Cache(mUrl);
        }
        callback.onStart(this);
        OkHttpClient client = HttpClient.getInstance();
        Request request = request();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Utils.e("http onFialure Exception:"+e.getMessage());
                if(call.isCanceled()){
                    Utils.e("call is canceled");
                    return;
                }
                postError(callback,new HttpException(e));
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if(call.isCanceled()){
                    Utils.e("call is canceled");
                    return;
                }
                if(!response.isSuccessful()){
                    postError(callback, new HttpException("http request error and error code is " + response.code()));
                    return;
                }
                String content = response.body().string();
                Map<String,String> headers = Utils.parseHeader(response.headers());
                Utils.i("http content: " + content);
                //添加缓存
                if(mCache!=null){
                    mCache.write(content);
                }
                try {
                    final T val = (T)parseObject(content);
                    postSuccess(callback,val,headers);
                } catch (Exception e) {
                    Utils.e("parse json string fail: " + content);
                    postError(callback, new HttpException("json parse json fail", e));
                }


            }
        });
        if(mCache!=null){
            String content = mCache.read();
            Utils.i("read cache " + content );
            try {
                return (T)parseObject(content);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private <T> void postSuccess(final RequestCallback<T> callback, final T val, final Map<String, String> headers) {
        mMainHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onSuccess(val,headers);
            }
        });
    }

    private <T> void postError(final RequestCallback<T> callback, final HttpException e) {
        mMainHandler.post(new Runnable() {
            @Override
            public void run() {
                callback.onFailure(e);
            }
        });
    }

    /**
     * 同步请求
     * */
    private <T> T realExecute(){
        if(METHOD_GET == mMethod){//只有get方法才有缓存
            mCache = new Cache(mUrl);
        }
        OkHttpClient client = HttpClient.getInstance();
        Request request = request();
        Call call = client.newCall(request);
        try {
            Response response = call.execute();
            if (!response.isSuccessful()) {
                Utils.e("response code is " + response.code());
                if(mCache!=null){
                    String content = mCache.read();
                    Utils.i("read cache " + content );
                    return (T)parseObject(content);
                }
                return null;
            }
            String content = response.body().string();
            if(mCache!=null){
                mCache.write(content);
            }
            return (T)parseObject(content);

        } catch (IOException e) {
            if(mCache!=null){
                String content = mCache.read();
                Utils.i("read cache " + content);
                try {
                    return (T)parseObject(content);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        } catch (Exception e) {
            Utils.e("http execute error:"+e.getMessage());
            e.printStackTrace();
            return null;
        }
        return null;
    }

    private <T> T parseObject(String content) throws Exception{
        if (mTargetClazz != null) {
            return  (T) JSON.parseObject(content, mTargetClazz);
        } else if (mTargetTypeReference != null) {
            return (T) JSON.parseObject(content, mTargetTypeReference);
        } else {
            return (T) content;
        }
    }

    private Request request() {
        HttpConfig config = HttpConfig.getInstance();
        mHeader.merge(config.commonHeader);
        mParam.merge(config.commonParam);
        Request.Builder builder = new Request.Builder().tag(mTag).headers(mHeader.convert());
        switch (mMethod) {
            case METHOD_GET:
                builder.url(Utils.spliceUrl(mUrl,mParam)).get();
                break;
            case METHOD_POST:
                builder.url(mUrl).post(mParam.body());
                break;
            case METHOD_PUT:
                builder.url(mUrl).put(mParam.body());
                break;
            case METHOD_DELETE:
                builder.url(mUrl).delete(mParam.body());
                break;
            case METHOD_HEAD:
                builder.url(mUrl).head();
                break;
            case METHOD_PATCH:
                builder.url(mUrl).patch(mParam.body());
                break;
            default:
                throw new IllegalStateException("unknown request method");
        }
        return builder.build();
    }



}
